using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
using System.Reflection;
using Microsoft.VisualBasic;
using System.Globalization;
using System.Diagnostics;

namespace vXCopy
{
	internal delegate void EventfileCreatedToProcess(object sender, FileToProcess e);

	public partial class MainApp : CmdLineSettingLib
	{

		internal event EventHandler<FileToProcess> EventfileCreatedToProcess;
		internal event EventHandler<FileToProcess> EventfileToProcess;
		internal event EventHandler<FileToProcess> EventfileErrorToProcess;
		internal event EventHandler<FileToProcess> EventfileProcessStatusToProcess;
		internal event EventHandler<FileToProcess> EventFileCompleted;

		static AutoResetEvent _autoEvent = new AutoResetEvent(false);
		internal System.Timers.Timer mainTimer = null;
		internal System.Timers.Timer copyTimer = null;
		internal bool bCopyTimerRunning = false;
		internal bool bFileOpRunning = false;

		public event EventHandler Done;
		private long _running = 0;
		private int nOffset = 0;
		string tmpPath = "";

		internal int mThreadCountNumber = 0;
		internal Boolean bStopProcess = false;

		internal DirectoryInfo ofromFolder;
		internal DirectoryInfo oDestFolder;

		Thread searchThread = null;
		//Thread copyThread = null;

		internal FileToProcessList fileToProcessList = null;

		frmFileProcess ofrmMain = null;

		//public MainApp()
		//{ }

		public MainApp(string[] args, frmFileProcess ProcessForm)
		{
			this.ofrmMain = ProcessForm;

			mainTimer = new System.Timers.Timer();
			mainTimer.Enabled = false;
			mainTimer.Interval = 200;
			mainTimer.Elapsed += new System.Timers.ElapsedEventHandler(mainTimer_Elapsed);

			copyTimer = new System.Timers.Timer();
			copyTimer.Interval = 200;
			copyTimer.Elapsed += new System.Timers.ElapsedEventHandler(copyTimer_Elapsed);

			if (args.Length == 0)
			{
#if(DEBUG)
				this.fromFolder = @"N:\Download\Microsoft\Whidbey";
				this.destFolder = @"C:\Download\Microsoft\Whidbey";
				this.MaxThreads = 6;

#endif

			}
			else
			{
				// Check to see if the user passed in a filename 
				if (args.Length < 2)
				{
					DisplayUsage();
					return;
				}
				if (args.Length > 3)
				{
					DisplayUsage();
					return;
				}

				if (args[0] == "/?")
				{
					DisplayUsage();
					return;
				}

				this.fromFolder = args[0];
				this.destFolder = args[1];

				if (!UtilLibrary.CheckFolder(this.fromFolder))
				{
					Debug.WriteLine("Folder [{}] doesn't exist", this.fromFolder);
					return;
				}

				if (args.Length == 3)
				{
					this.MaxThreads = Int32.Parse(args[2]);
					if (this.MaxThreads < 1 || this.MaxThreads > 15)
					{
						DisplayUsage();
						return;
					}
				}

				if (!UtilLibrary.CheckCreateFolder(this.destFolder, false))
				{
					Debug.WriteLine("Process interrupted by User");
					return;
				}
			}

			//Debug.WriteLine("");
			//Debug.WriteLine("");
			//Debug.WriteLine("----------------------------------------------------------------------");
			//Debug.WriteLine("\t{0}\t\tVersion:{1}", "vxCopy", Assembly.GetExecutingAssembly().GetName().Version);
			//Debug.WriteLine("----------------------------------------------------------------------");
			//Debug.WriteLine("\t(Copyright Vincent RITHNER 2005 - All Rights Reserved)");
			//Debug.WriteLine("");
			//Debug.WriteLine("\tCopy Folder/Files Recursively in MultiTreads mode");
			//Debug.WriteLine("");
			//Debug.WriteLine("----------------------------------------------------------------------");
			//Debug.WriteLine("\tStarted at: \t\t" + DateTime.Now.ToLongDateString());
			//Debug.WriteLine("");
			//Debug.WriteLine("\tSource: \t\t{0}", this.fromFolder);
			//Debug.WriteLine("\tDestination: \t\t{0}", this.destFolder);
			//Debug.WriteLine("");
			//Debug.WriteLine("\tIncludes Files: \t{0}", "*.*");
			//Debug.WriteLine("\tExcludes Files: \t{0}", "");
			//Debug.WriteLine("");
			//Debug.WriteLine("\tOptions:");
			//Debug.WriteLine("\t\tMaxTreads: \t{0}", this.MaxThreads);
			//Debug.WriteLine("");
			//Debug.WriteLine("----------------------------------------------------------------------");

			Application.DoEvents();
			ofrmMain.txtSourcePath.Text = this.fromFolder;
			ofrmMain.txtDestPath.Text = this.destFolder;
			ofrmMain.txtInclude.Text = "*.*";
			ofrmMain.txtExclude.Text = "";
			ofrmMain.txtThreadMax.Text = this.MaxThreads.ToString();

			Application.DoEvents();
		}



		private static void DisplayUsage()
		{
			// User did not provide enough parameters. 
			// Display usage: 
			Debug.WriteLine("");
			Debug.WriteLine("vxCopy (Copyright Vincent RITHNER 2004 - All Rights Reserved)");
			Debug.WriteLine("");
			Debug.WriteLine("Copy Folder/Files Recursively in MultiTreads mode");
			Debug.WriteLine("");
			Debug.WriteLine("vxCopy FromFolder DestFolder [Thread #]");
			Debug.WriteLine("Usage: vxCopy FromFolder DestFolder 5");
			Debug.WriteLine("");
			Debug.WriteLine("Thread # (Min= 1 / Max=15)-(Default= 4");
			Debug.WriteLine("");
		}

		private void StopSearch()
		{
			if (this != null)
				this.Stop();
		}

		private void delDirFound(object sender, EventArgs e)
		{
			try
			{
				//this.BeginInvoke(new EventHandler(DirFound), new object[] { sender });
				this.DirFound(sender, e);
			}
			catch (InvalidOperationException) //user closed the form, so the this handle is no longer valid
			{
				StopSearch();
			}
		}

		private void delFileFound(object sender, EventArgs e)
		{
			try
			{
				//this.BeginInvoke(new EventHandler(FileFound), new object[] { sender });
				this.FileFound(sender, e);
			}
			catch (InvalidOperationException) //user closed the form, so the this handle is no longer valid
			{
				StopSearch();
			}
		}

		private void DirFound(object sender, EventArgs e)
		{
			try
			{
				//DirectoryInfo dir = new DirectoryInfo((string)sender);
				//Console.CursorLeft = 0;
				//Console.ForegroundColor = ConsoleColor.DarkCyan;
				//Debug.WriteLine(string.Format(@"{0}\t{1}\t{2}", dir.FullName, dir.GetFiles().Length.ToString(), dir.LastWriteTime.ToShortDateString()));
				//Debug.WriteLine("----------------------------------------------------------------------------------");
				Application.DoEvents();
			}
			catch (Exception ex)
			{
				Debug.WriteLine("DirFound: " + ex.Message);
			}
		}

		private void FileFound(object sender, EventArgs e)
		{
			try
			{
				//FileInfo file = new FileInfo((string)sender);
				//Console.CursorLeft = 0;
				//Console.ForegroundColor = ConsoleColor.DarkCyan;
				//Debug.WriteLine(string.Format(@"{0}\t{1}\t{2}", file.FullName, file.Length.ToString(), file.LastWriteTime.ToShortDateString()));
				Application.DoEvents();
			}
			catch (Exception ex)
			{
				Debug.WriteLine("FileFound: " + ex.Message);
			}
		}

		void delSearchDone(object sender, EventArgs e)
		{
			try
			{
				//this.BeginInvoke(new EventHandler(SearchDone));
				this.SearchDone(sender, e);
			}
			catch (InvalidOperationException) //user closed the form, so the this handle is no longer valid
			{
			}

		}

		void SearchDone(object sender, EventArgs e)
		{
			Debug.WriteLine("Searched All Files Done....");
			Debug.WriteLine("Ready.");

		}

		public void Start()
		{
			//this.DirFound += new EventHandler(this.delDirFound);
			//this.FileFound += new EventHandler(this.delFileFound);
			this.Done += new EventHandler(this.delSearchDone);

			//Start copy
			this._running = 1;
			this.ProcessCopy();
		}

		public bool ProcessCopy()
		{
			try
			{

				ofromFolder = new DirectoryInfo(this.fromFolder);
				oDestFolder = new DirectoryInfo(this.destFolder);

				if (!UtilLibrary.CheckFolder(ofromFolder))
				{
					Debug.WriteLine("Source Folder [{0}] doesn't exist !", fromFolder);
					return false;
				}

				ProcessGetAllFiles();
				return true;
			}
			finally
			{
			}
		}

		public void Stop()
		{
			if (System.Threading.Interlocked.Read(ref _running) > 0)
			{
				System.Threading.Interlocked.Exchange(ref _running, 0);
				_autoEvent.WaitOne();
				mainTimer.Stop();
			}
		}

		/// <summary>
		/// mainTimer_Elapsed
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void mainTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
		{
			try
			{
				if (!bCopyTimerRunning && fileToProcessList.FilesList.Count > 0)
				{
					copyTimer.Start();
					return;
				}

				if (bCopyTimerRunning)
				{
					if (mThreadCountNumber > MaxThreads)
					{
						Debug.WriteLine("Process Stopped...");
						copyTimer.Start();
						return;
					}

					if (mThreadCountNumber <= MaxThreads)
					{
						Debug.WriteLine("Process Resumed...");
						copyTimer.Start();
						return;
					}

				}
			}
			catch (Exception ex)
			{

				Debug.WriteLine("mainClass::mainTimer_Elapsed" + ex.Message);
			}
		}

		internal void ProcessGetAllFiles()
		{

			try
			{
				fileToProcessList = new FileToProcessList();

				//ThreadStart delSearchThread = new ThreadStart(this.SearchFilesThread);
				//ThreadStart delCopyThread = new ThreadStart(this.CopyFilesThread);

				//searchThread = new Thread(delSearchThread);
				//copyThread = new Thread(delCopyThread);

				System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(SearchFilesThread));

				//searchThread.IsBackground = true;

				//searchThread.Start();

				mainTimer.Start();

				//Thread.Sleep(600);

				//searchThread.Join();

			}

			catch (Exception ex)
			{
				Debug.WriteLine("ProcessGetAllFiles: " + ex.Message);
			}
			finally
			{
				//copyThread = null;
				//searchThread = null;
			}

		}

		private void SearchFilesThread(Object stateInfo)
		{
			//GetAllFiles(this.ofromFolder.FullName, this.oDestFolder.FullName, true);
			GetAllFiles(this.fromFolder, this.destFolder, true);
		}

		void copyTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
		{
			try
			{
				if (bFileOpRunning || mThreadCountNumber > MaxThreads)
					return;

				bFileOpRunning = true;

				if (fileToProcessList.FilesList.Count == 0)
				{
					copyTimer.Stop();
					copyTimer.Enabled = false;
					return;
				}

				if (System.Threading.Interlocked.Read(ref _running) > 0)
				{
					//Check if we must stop processing files
					if (this.bStopProcess)
					{
						copyTimer.Stop();
						copyTimer.Enabled = false;
						return;
					}
				}

				FileToProcess file = fileToProcessList.FilesList[0];

				mThreadCountNumber++;
				Debug.WriteLine("Thread#: "+ mThreadCountNumber.ToString());

				//ofrmMain.txtExclude.Text = mThreadCountNumber.ToString();
				//ofrmMain.txtExclude.Refresh();

				ProcessFileCopy fileToCopy = new ProcessFileCopy(file, mThreadCountNumber);

				fileToCopy.FileCompleted += new EventHandler<FileToProcess>(fileToCopy_fileCompleted);
				fileToCopy.EventfileCreatedToProcess += new EventHandler<FileToProcess>(fileToCopy_EventfileCreatedToProcess);
				fileToCopy.EventfileToProcess += new EventHandler<FileToProcess>(fileToCopy_EventfileToProcess);
				fileToCopy.EventfileErrorToProcess += new EventHandler<FileToProcess>(fileToCopy_EventErrorfileToProcess);
				fileToCopy.EventfileProcessStatusToProcess += new EventHandler<FileToProcess>(fileToCopy_EventfileProcessStatusToProcess);

				//this.EventfileCreatedToProcess += new EventHandler<FileToProcess>(ofrmMain.fileToCopy_EventfileCreatedToProcess);
				//this.EventfileToProcess += new EventHandler<FileToProcess>(ofrmMain.fileToCopy_EventfileToProcess);
				//this.EventfileErrorToProcess += new EventHandler<FileToProcess>(ofrmMain.fileToCopy_EventErrorfileToProcess);
				//this.EventfileProcessStatusToProcess += new EventHandler<FileToProcess>(ofrmMain.fileToCopy_EventfileProcessStatusToProcess);
				//this.EventFileCompleted += new EventHandler<FileToProcess>(ofrmMain.fileToCopy_fileCompleted);

				fileToCopy.StartCopy();

				Monitor.Enter(fileToProcessList);
				fileToProcessList.FilesList.Remove(file);
				Monitor.Exit(fileToProcessList);

				Thread.Sleep(100);
	
			}

			catch (Exception ex)
			{

				Debug.WriteLine(ex.Message);
			}
			finally
			{
				bFileOpRunning = false;
			}
		}

		void fileToCopy_EventfileCreatedToProcess(object sender, FileToProcess e)
		{
			//EventfileCreatedToProcess(sender, e);
			ofrmMain.fileToCopy_EventfileCreatedToProcess(sender, e);
		}

		void fileToCopy_EventfileProcessStatusToProcess(object sender, FileToProcess e)
		{
			//Debug.WriteLine(string.Format("({0})", e.ProcessStatus));
			Debug.WriteLine(string.Format("File: {0}{5}{1}{5}{2}{5}[{3}]{5}({4}({6}))", e.oFileDestInfo.Name, e.oFileDestInfo.Length.ToString(), e.oFileDestInfo.LastAccessTime, e.ItemStatus, e.ProcessStatus, Constants.vbTab, e.ProcessStatus));
			//EventfileProcessStatusToProcess(sender, e);
			ofrmMain.fileToCopy_EventfileProcessStatusToProcess(sender, e);
		}

		void fileToCopy_EventErrorfileToProcess(object sender, FileToProcess e)
		{
			Debug.WriteLine(string.Format("File Error: {0}{5}{1}{5}{2}-->{3}-{4}", e.destFile, e.oFileSourceInfo.Length.ToString(), e.oFileSourceInfo.LastAccessTime, e.ErrorStatus, e.ErrorStack, Constants.vbTab));
			//EventfileErrorToProcess(sender, e);
			ofrmMain.fileToCopy_EventErrorfileToProcess(sender, e);
		}

		void fileToCopy_EventfileToProcess(object sender, FileToProcess e)
		{
			Debug.WriteLine(string.Format("File: {0}{5}{1}{5}{2}{5}[{3}]{5}({4})", e.oFileDestInfo.Name, e.oFileSourceInfo.Length.ToString(), e.oFileSourceInfo.LastAccessTime, e.ItemStatus, e.ProcessStatus, Constants.vbTab + Constants.vbTab));
			ofrmMain.fileToCopy_EventfileToProcess(sender, e);
		}

		void fileToCopy_fileCompleted(object sender, FileToProcess e)
		{
			mThreadCountNumber--;
			//EventFileCompleted(sender, e);
			ofrmMain.fileToCopy_fileCompleted(sender, e);


		}

		void GetAllFiles(string fromDir, string destDir, bool recursive)
		{
			DateTimeFormatInfo dfi = new DateTimeFormatInfo();
			dfi.FullDateTimePattern = "dd/mm/yyyy -- " + dfi.ShortTimePattern;

			try
			{
				nOffset += 1;
				//Thread.CurrentThread.Priority = ThreadPriority.Lowest;
				string[] _files = Directory.GetFiles(fromDir);
				string[] _dirs = Directory.GetDirectories(fromDir);

				//
				foreach (string subDir in _dirs)
				{
					if (System.Threading.Interlocked.Read(ref _running) > 0)
					{
						{
							delDirFound(subDir, new EventArgs());
						}

						///create the path to the directory in destdir
						tmpPath = Path.Combine(destDir, UtilLibrary.GetFileNameOnly(subDir));

						FileToProcess fileToProcess = new FileToProcess();
						fileToProcess.ItemStatus = "Crawling";
						fileToProcess.ItemNumber = _files.Length;

						DirectoryInfo tmpDestFolder = new DirectoryInfo(tmpPath);

						//recursively call this function over and over again with each new dir.
						GetAllFiles(subDir, tmpDestFolder.FullName, recursive);

						
					}
					else
					{
						break;
					}
				}

				foreach (string file in _files)
				{

					if (System.Threading.Interlocked.Read(ref _running) > 0)
					{

						//Check if we must stop processing files
						if (this.bStopProcess)
						{
							break;
						}

						tmpPath = Path.Combine(destDir, new FileInfo(file).Name);
						{
							FileFound(file, new EventArgs());


							FileToProcess ofile = new FileToProcess();
							ofile.fromFile = file;
							ofile.destFile = tmpPath;
							ofile.destFolder = destDir;
							ofile.fromFolder = fromDir;
							ofile.MaxThreads = MaxThreads;

							Monitor.Enter(fileToProcessList);
							fileToProcessList.FilesList.Add(ofile);
							Monitor.Exit(fileToProcessList);


							Debug.WriteLine("FileList# " + fileToProcessList.FilesList.Count.ToString());
						}

						System.Threading.Thread.Sleep(10);
						Application.DoEvents();
					}

					else
					{
						break;
					}
				}
			}

			catch (Exception ex)
			{
				//throw ex;
				Debug.WriteLine("File:" + tmpPath + " " + ex.Message);
			}
			finally
			{
				_autoEvent.Set();
			}

			nOffset -= 1;
			if (nOffset <= 0)
				if (Done != null)
				{
					Done(this, new EventArgs());
				}
		}

	}
}
